# coding=utf-8
import numpy as np
from FtpController import *
import joblib
class MimicryHoneypot:
    __ftp_controller = None
    __current_state = None
    __value_space = {				# 定义每个状态分量的取值空间
        'CHROOT':FtpController.get_config_space('chroot_local_user'),
        'UPLOAD':FtpController.get_config_space('anon_upload_enable'),
        'ANON':FtpController.get_config_space('anonymous_enable'),
        'FILE':FtpController.get_config_space('anon_root'),
        'PORT':FtpController.get_config_space('listen_port')
    }
    __last_reward = 0				# 系统上一次演化后得到的外界的奖励值
    
    def set_state(self):
        self.__current_state['CHROOT'] = 10
    def __init__(self,mode='sim',config_file_path = '/etc/vsftpd.conf'):		# 读取当前系统的ftp配置文件，生成状态向量
        self.__current_state = {			# 定义默认的状态向量
        'CHROOT':'YES',
        'UPLOAD':'YES',
        'ANON':'YES',
        'FILE':'/srv/ftp0',
        'PORT':'21'
        }
        self.__ftp_controller = FtpController(mode,config_file_path)
        configs = self.__ftp_controller.cur_configs
        self.__current_state['CHROOT'] = configs['chroot_local_user']
        self.__current_state['UPLOAD'] = configs['anon_upload_enable']
        self.__current_state['ANON'] = configs['anonymous_enable']
        self.__current_state['FILE'] = configs['anon_root']
        self.__current_state['PORT'] = configs['listen_port']

        self.__ftp_controller.start_vsftpd()

    def __apply_state(self):  	# 根据当前的状态向量来配置ftp服务
        if self.__ftp_controller.mode == 'sim': return 
        configs = self.__ftp_controller.cur_configs

        # chroot
        configs['chroot_local_user'] = self.__current_state['CHROOT']
        # upload
        configs['anon_upload_enable'] = self.__current_state['UPLOAD']
        configs['anon_mkdir_write_enable'] = self.__current_state['UPLOAD']
        configs['anon_other_write_enable'] = self.__current_state['UPLOAD']
        # anon
        configs['anonymous_enable'] = self.__current_state['ANON']
        # file
        configs['anon_root'] = self.__current_state['FILE']
        # port 
        configs['listen_port'] = self.__current_state['PORT']
        self.__ftp_controller.set_configs(configs)
        exceptions = [
            'chroot_local_user',
            'anon_upload_enable','anon_mkdir_write_enable','anon_other_write_enable'
            'anonymous_enable',
            'anon_root',
            'anon_root'
        ]
 
        # 随机演化其他的ftp特征(ip地址，欢迎语文件)
        self.__ftp_controller.random_change(exceptions)

    def __get_value_index(self,key):    
        space = self.__value_space[key]
        len_of_space = len(space)
        for i in range(len_of_space):
            if self.__current_state[key] == space[i]:
                return i
        return -1
    
    def getCurrentConfig(self):
        ip = self.__ftp_controller.ip
        configs = self.__ftp_controller.cur_configs
        return ip,configs
        
    def currentState(self):   # 以分量取值下标的形式返回当前的状态向量
        state = []
        state.append(self.__get_value_index('CHROOT'))
        state.append(self.__get_value_index('UPLOAD'))
        state.append(self.__get_value_index('ANON'))
        state.append(self.__get_value_index('FILE'))
        state.append(self.__get_value_index('PORT'))
        return np.array(state)
    @property
    def action_space_len(self):
        sum = 0
        for i in range(len(self.__current_state)):
            sum += 2 ** i
        return sum + 1
    @property
    def pkt_num(self):
        return self.__ftp_controller.pkts
    def pseudo_step(self,config_path):
        all_config = joblib.load(config_path)
        ip = all_config['ip']
        configs = all_config['configs']
        self.__ftp_controller.set_configs(configs)
        self.__ftp_controller.change_ip(ip)
        
    def step(self,action,muschange = False):    # 执行一个动作，执行以此演化
        '''do one action by action id

        Parameters
        --------------
        action:int
            one action's binary serialization coresponds to one state,each state component coresponds to a bit in the binary serialization.
            bit 1 means change, bit 0 means do not change.CHROOT coresponds to the lowest bit.
        Returns
        ---------
        next_state:array
            the state reached after the action
        rewared:float
            the reward caused by action
        done_flag:int (default = 0)

        '''

        for each in self.__current_state:
            change = action % 2
            action = int(action / 2)
            space = self.__value_space[each]

            if change == 1:
                random_index = random.randint(0,len(space) - 1)
                if self.__current_state[each] == space[random_index]:
                    random_index = (random_index + 1) % len(space)
                self.__current_state[each] = space[random_index]

        self.__apply_state()
        next_state = self.currentState()
            
        return next_state,self.__last_reward,0

    